#include "functions.h"
#include <cuda_runtime.h>
#include <assert.h>
#include <helper_cuda.h>
#include <stdio.h>


/**
 * makes a copy of an uchar4's array
 * @param a original array
 * @param width size X
 * @param height size Y
 * @return a copy of a
 */
uchar4* copy(uchar4* a, int width, int height){
	uchar4* b = (uchar4*)calloc(width*height,sizeof(uchar4));
	for(int i=0;i<height;i++){
		for(int j=0;j<width;j++){
			b[i*width+j].x=a[i*width+j].x;
			b[i*width+j].y=a[i*width+j].y;
			b[i*width+j].z=a[i*width+j].z;
			b[i*width+j].w=0;
			}
		}
	return b;
}


bool compareUchar4(uchar4* buf1,uchar4* buf2,int width, int height){
	assert(buf1!=NULL && buf2!=NULL);
	for(int i=0;i<height;i++){
		for(int j=0;j<width;j++){
			if(!(buf1[i*width+j].x==buf2[i*width+j].x &&
					buf1[i*width+j].y==buf2[i*width+j].y &&
					buf1[i*width+j].z==buf2[i*width+j].z)){
				printf("(col %d,fil %d)\n",j,i);
				return false;
			}
		}
	}
	return true;
}
/**
 * compare two integer's array
 * PRE:buf2 != NULL && buf1 != NULL && buf1 and buf2 have space for width*height integers
 * @param buf1 integer's array
 * @param buf2 integer's array
 * @param width integer
 * @param height integer
 * @return case equal true
 * 		   		else false
 */
bool compare(int* buf1,int* buf2,int width, int height){
	assert(buf1!=NULL && buf2!=NULL);
	for(int i=0;i<height;i++){
		for(int j=0;j<width;j++){
			if(buf1[i*width+j]!=buf2[i*width+j]){
				printf("%d %d\n",i,j);
				return false;
			}
		}
	}
	return true;
}
/**
 *
 * @return the numbers of CUDA's devices that exist in the machine
 */
int chooseCudaDevice(){
	int deviceCount = 0;
	int maxThreadsPerBlock = 0;
	int MaxBlockDimX = 0;
	int MaxBlockDimY = 0;
	int MaxBlockDimZ = 0;
	cudaDeviceProp deviceProp;
    cudaError_t error_id = cudaGetDeviceCount(&deviceCount);

    if (error_id != cudaSuccess)
    {
        printf("cudaGetDeviceCount returned %d\n-> %s\n", (int)error_id, cudaGetErrorString(error_id));
        exit(EXIT_FAILURE);
    }

    // This function call returns 0 if there are no CUDA capable devices.
    if (deviceCount == 0)
    {
        printf("There are no available device(s) that support CUDA\n");
		exit(EXIT_FAILURE);
    }
	for (int dev = 0; dev < deviceCount; ++dev)
    {
		cudaSetDevice(dev);
		cudaGetDeviceProperties(&deviceProp,dev);
		cudaDeviceGetAttribute ( &maxThreadsPerBlock, cudaDevAttrMaxThreadsPerBlock, dev );
		cudaDeviceGetAttribute ( &MaxBlockDimX, cudaDevAttrMaxBlockDimX, dev );
		cudaDeviceGetAttribute ( &MaxBlockDimY, cudaDevAttrMaxBlockDimY, dev );
		cudaDeviceGetAttribute ( &MaxBlockDimZ, cudaDevAttrMaxBlockDimZ, dev );
		//printf("sharedMemPerBlock  %.0f MBytes\n,sizeof(int) %d\n", (float)deviceProp.sharedMemPerBlock,(int)sizeof(int));
		printf("Detected %d CUDA Capable device(s)\n", deviceCount);
		printf("\nDevice %d: \"%s\"\n", 0, deviceProp.name);
		printf("  CUDA Capability Major/Minor version number:    %d.%d\n", deviceProp.major, deviceProp.minor);
		printf("  CUDA maximum threads per block:		 %d\n",maxThreadsPerBlock);
		printf("  CUDA Maximum x-dimension of a block:		 %d\n",MaxBlockDimX);
		printf("  CUDA Maximum y-dimension of a block:		 %d\n",MaxBlockDimY);
		printf("  CUDA Maximum z-dimension of a block:		 %d\n",MaxBlockDimZ);
		printf("  Total Streaming multiprocesors on device:	 %d\n",(int)deviceProp.multiProcessorCount);
		char msg[256];
        sprintf(msg, "  Total amount of global memory:                 %.0f MBytes (%llu bytes)\n",
                (float)deviceProp.totalGlobalMem/1048576.0f, (unsigned long long) deviceProp.totalGlobalMem);
        printf("%s", msg);

        printf("  (%2d) Multiprocessors x (%3d) CUDA Cores/MP:    %d CUDA Cores\n",
               deviceProp.multiProcessorCount,
               _ConvertSMVer2Cores(deviceProp.major, deviceProp.minor),
               _ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) * deviceProp.multiProcessorCount);
        printf("  GPU Clock rate:                                %.0f MHz (%0.2f GHz)\n", deviceProp.clockRate * 1e-3f, deviceProp.clockRate * 1e-6f);

		if(CUDART_VERSION >= 5000){
			// This is supported in CUDA 5.0 (runtime API device properties)
			printf("  Memory Clock rate:                             %.0f Mhz\n", deviceProp.memoryClockRate * 1e-3f);
			printf("  Memory Bus Width:                              %d-bit\n",   deviceProp.memoryBusWidth);
			}
		printf("\n\n**************************************\n\n");
	}

	return deviceCount;

}
/**
 * Convert buf in an image. The image's name is filename
 * PRE: buf != NULL && buf has space for width*height integers && filename != NULL
 * @param filename string
 * @param buf  integer's array
 * @param width integer
 * @param height integer
 */
void toImage(char* filename, int* buf,int width,int height){
	unsigned char* data=(unsigned char*)calloc(width*height*4,sizeof(uchar4));
	for(int i=0;i<height;i++){
		for(int j=0;j<width;j++){
			int index = 4*(i * width + j);
			if(buf[i*width+j]==0){
				data[index] = 0;
				data[index+1] = 0;
				data[index+2] = 0;
				data[index+3] = 0;
			}
			else{
				int p=0;
				if(buf[i*width+j]%255==1){
					p=50;
				}
				if(buf[i*width+j]==804){
					data[index] = (unsigned char)255;
					data[index+1] = 0;
					data[index+2] = 0;
					data[index+3] = 0;
				}
				else if(buf[i*width+j]==1024){
					data[index] = 0;
					data[index+1] = 255;
					data[index+2] = 0;
					data[index+3] = 0;
				}
				else{
				data[index] = (unsigned char)(buf[i*width+j]+p)%255;
				data[index+1] = (unsigned char)(((int)data[index])*((int)data[index]))%255;
				data[index+2] = (unsigned char)(((int)data[index+1])*((int)data[index+1]))%255;
				data[index+3] = 0;
				}
			}
		}
	}
	sdkSavePPM4ub(filename,data,width,height);
	free(data);

}
